//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <elatypes.h>
#include <prxstub.h>
#include <rot.h>
#include <elapi.h>
#include <clsinfo.h>
#include <eladef.h>
#include <locmod.h>
#include <critsec.h>

#if (_TEST_TYPE == 4) /* Test for CAR performance */
#include "_kcartstabrg.cpp"
#endif // _TEST_TYPE == 4

#if (_TEST_TYPE == 7) /* Test for Marshaling */
#include "_kmarshaltstabrg.cpp"
#endif // _TEST_TYPE == 7

#define DLL_PROCESS_DETACH 0
#define DLL_PROCESS_ATTACH 1
#define DLL_THREAD_ATTACH  2
#define DLL_THREAD_DETACH  3

extern CIClassInfo *g_elasys_classInfo;
extern IKernel *g_pIKernel;
extern IProcess * g_pICurrentProcess;
extern IDeviceDriver *g_pConsole;
extern CriticalSection g_moduleLock;
extern void InitTLS();
extern void UninitTLS();
extern UInt32 g_uMainThreadStackTop;
extern IHostingManager *g_pHostingManager;
extern DLinkNode g_LocModList;
extern CriticalSection g_LocModListLock;
extern CriticalSection g_importTableLock;
extern CriticalSection g_exportTableLock;
extern CriticalSection g_marshalLock;
extern CriticalSection g_staticObjectListLock;
extern CriticalSection g_processExitRoutineListLock;

EXTERN_C ECode InitCConsoleIo();
EXTERN_C ECode UninitCConsoleIo();

EXTERN UInt32 GetCurrentThreadStackTop();
EXTERN void CleanUpThreadLocalSlots(UInt32 uStackTop);

EXTERN ECode InitializeThreadInfo();

UInt32 g_uMainThreadStackTop = 0;

EXTERN_C _ELASTOS Boolean __stdcall DllMain(
    PVoid hDllHandle,
    UInt32 dwReason,
    PVoid preserved)
{
    static CIClassInfo * s_pKerClassInfo = NULL;
#if (_TEST_TYPE == 4) /* Test for CAR performance */
    static CIClassInfo * s_pCarTst_classInfo = NULL;
#endif // _TEST_TYPE == 4

#if (_TEST_TYPE == 7) /* Test for Marshaling */
    static CIClassInfo * s_pkmarshaltst_classInfo = NULL;
#endif // _TEST_TYPE == 7

    ECode ec;

    switch (dwReason) {
        case DLL_PROCESS_ATTACH:
            g_uMainThreadStackTop = GetCurrentThreadStackTop();

            ec = _CCriticalSection_Initialize(&g_moduleLock);
            if (FAILED(ec)) {
                printf("In DLLMain, _CCriticalSection_Initialize failed."
                    " ec = 0x%08x\n", ec);
                return FALSE;
            }

            InitTLS();

            ec = _CCriticalSection_Initialize(&g_staticObjectListLock);
            if (FAILED(ec)) {
                printf("In DLLMain, _CCriticalSection_Initialize failed."
                    " ec = 0x%08x\n", ec);
                return FALSE;
            }

            ec = _CCriticalSection_Initialize(&g_processExitRoutineListLock);
            if (FAILED(ec)) {
                printf("In DLLMain, _CCriticalSection_Initialize failed."
                    " ec = 0x%08x\n", ec);
                return FALSE;
            }

            ec = _CCriticalSection_Initialize(&g_LocModListLock);
            if (FAILED(ec)) {
                printf("In DLLMain, _CCriticalSection_Initialize failed."
                    " ec = 0x%08x\n", ec);
                return FALSE;
            }

            ec = InitializeROT();
            if (FAILED(ec)) {
                printf("In DLLMain, InitializeROT failed"
                    " ec = 0x%08x\n", ec);
                return FALSE;
            }

            ec = InitServerClassInfo();
            if (FAILED(ec)) {
                printf("In DLLMain, InitServerClassInfo failed"
                    " ec = 0x%08x\n", ec);
                return FALSE;
            }

            s_pKerClassInfo = (CIClassInfo *)\
                _CMemoryHeap_Alloc(g_elasys_classInfo->totalSize, FALSE);

            RelocateClassInfo(g_elasys_classInfo, s_pKerClassInfo);

            ec = RegisterServerClassInfo(s_pKerClassInfo);
            if (FAILED(ec)) {
                printf("In DLLMain, RegisterServerClassInfo 0 failed"
                    " ec = 0x%08x\n", ec);
                return FALSE;
            }

            ec = InitCConsoleIo();
            if (FAILED(ec)) {
                printf("In DLLMain, InitCConsoleIo failed"
                    " ec = 0x%08x\n", ec);
                return FALSE;
            }

#if (_TEST_TYPE == 4) /* Test for CAR performance */
            s_pCarTst_classInfo =
                (CIClassInfo *)_CMemoryHeap_Alloc(
                    ((CIClassInfo*)g_kcartst_classInfo)->totalSize, FALSE);
            RelocateClassInfo(g_kcartst_classInfo, s_pCarTst_classInfo);
            ec = RegisterServerClassInfo((CIClassInfo *)s_pCarTst_classInfo);
            if (FAILED(ec)) {
                printf("In DLLMain, RegisterServerClassInfo 1 failed"
                    " ec = 0x%08x\n", ec);
                return FALSE;
            }
#endif // _TEST_TYPE == 4

#if (_TEST_TYPE == 7) /* Test for Marshaling */
            s_pkmarshaltst_classInfo = \
                (CIClassInfo *)_CMemoryHeap_Alloc(
                    ((CIClassInfo*)g_kmarshaltst_classInfo)->totalSize, FALSE);
            RelocateClassInfo(g_kmarshaltst_classInfo,
                s_pkmarshaltst_classInfo);
            ec = RegisterServerClassInfo(
                (CIClassInfo *)s_pkmarshaltst_classInfo);
            if (FAILED(ec)) {
                printf("In DLLMain, RegisterServerClassInfo 2 failed"
                    " ec = 0x%08x\n", ec);
                return FALSE;
            }
#endif // _TEST_TYPE == 7

            // Hold the interface pointer to the current process
            //
            ec = g_pIKernel->GetCurrentProcess(&g_pICurrentProcess);
            if (FAILED(ec)) {
                printf("In DLLMain, GetCurrentProcess failed"
                    " ec = 0x%08x\n", ec);
                return FALSE;
            }

            // Hold the interface pointer to the current thread
            //
            ec = InitializeThreadInfo();
            if (FAILED(ec)) {
                printf("In DLLMain, InitializeThreadInfo() failed"
                    " ec = 0x%08x\n", ec);
                return FALSE;
            }

            break;

        case DLL_PROCESS_DETACH:
            UninitCConsoleIo();

            DLinkNode * pLocalModule;
            if (NULL != g_pIKernel) {
                g_pIKernel->Release();
            }
            if (NULL != g_pICurrentProcess) {
                g_pICurrentProcess->Release();
            }
            if (NULL != g_pHostingManager) {
                g_pHostingManager->Release();
            }
            if (NULL != g_pConsole) {
                g_pConsole->Release();
            }

            UninitTLS();

            _CCriticalSection_Terminate(&g_staticObjectListLock);
            _CCriticalSection_Terminate(&g_processExitRoutineListLock);

            _CCriticalSection_Terminate(&g_moduleLock);

            pLocalModule = g_LocModList.Next();
            while (pLocalModule != &g_LocModList) {
                pLocalModule->Detach();
                ((LocalModule *)pLocalModule)->m_pIModule->Release();
                delete ((LocalModule *)pLocalModule)->m_pwszUunm;
                delete pLocalModule;
                pLocalModule = g_LocModList.Next();
            }
            _CCriticalSection_Terminate(&g_LocModListLock);
            _CCriticalSection_Terminate(&g_importTableLock);
            _CCriticalSection_Terminate(&g_exportTableLock);
            _CCriticalSection_Terminate(&g_marshalLock);
#ifdef MEMORYSPY
            UnregisterServerClassInfos();
            _CMemoryHeap_Free(s_pKerClassInfo);
#if (_TEST_TYPE == 4) /* Test for CAR performance */
            _CMemoryHeap_Free(s_pCarTst_classInfo);
#endif // _TEST_TYPE == 4

#if (_TEST_TYPE == 7) /* Test for Marshaling */
        _CMemoryHeap_Free(s_pkmarshaltst_classInfo);
#endif // _TEST_TYPE == 7
#endif
            // Do something
            // TODO: here need recycle the memory used by ezCOM
            break;
    }

    return TRUE;
}

EXTERN_C ECode __stdcall CrtFindService(WString wszName, IInterface **ppObj)
{
    return _CSystem_FindRunningObject(wszName, ppObj);
}

EXTERN_C ECode /* IApplet* */ __stdcall CrtGetCurrentAppletDir(WStringBuf* pwsAppletDir)
{
    IApplet* pApplet = NULL;
    ECode ec = _CApplet_GetCurrent((PInterface*)&pApplet);
    if (FAILED(ec)) return ec;

    ec = pApplet->GetCurrentDirectory(pwsAppletDir);
    pApplet->Release();
    return ec;
}

EXTERN_C ECode CrtInitializeLock(PVoid* ppLock)
{
	CCriticalSection *pLock = new CCriticalSection();
    if (pLock == NULL) {
        return E_OUT_OF_MEMORY;
    }

    *ppLock = pLock;

    return pLock->Initialize();
}

EXTERN_C ECode CrtEnterLock(PVoid pLock)
{
	if (pLock == NULL)
		return E_INVALID_OPERATION;

    CCriticalSection *lock = (CCriticalSection*)pLock;
	lock->Enter();

    return NOERROR;
}

EXTERN_C ECode CrtLeaveLock(PVoid pLock)
{
	if (pLock == NULL)
		return E_INVALID_OPERATION;

    CCriticalSection *lock = (CCriticalSection*)pLock;
    lock->Leave();

    return NOERROR;
}

EXTERN_C ECode CrtDeleteLock(PVoid* ppLock)
{
	if (!ppLock || !*ppLock)
		return E_INVALID_OPERATION;

    CCriticalSection *pLock = (CCriticalSection*)(*ppLock);
    pLock->Terminate();
    delete pLock;
    *ppLock = NULL;

	return NOERROR;
}
